package com.burukeyou.smartdi.config;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.util.StringUtils;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 一个工具类，用于访问 Spring 应用程序上下文并管理 Bean 的别名。
 * 该类实现了 ApplicationContextAware 接口，用于在运行时设置静态 springContext。
 * @author  caizhihao
 */
public abstract class SpringBeanContext implements ApplicationContextAware {

    // 静态的 ApplicationContext 实例，用于访问 Spring 上下文中的 Bean。
    protected static ApplicationContext springContext;

    // 用于存储 Bean 别名及其对应的原始 Bean 名称的映射。
    private static final Map<String,String> BEAN_SPI_NAME = new ConcurrentHashMap<>();

    /**
     * 为给定的 Bean 名称注册一个别名。
     * 如果别名已存在，则抛出异常以避免重复注册。
     *
     * @param beanName 原始的 Bean 名称
     * @param aliasName 要注册的别名
     * @throws RuntimeException 如果别名已存在，抛出异常
     */
    public static void registerBeanAlias(String beanName,String aliasName){
        if (BEAN_SPI_NAME.containsKey(aliasName)){
            throw new RuntimeException("bean name ["+aliasName+"] already exist");
        }
        BEAN_SPI_NAME.put(aliasName,beanName);
    }

    /**
     * 根据 Bean 的名称从应用程序上下文中获取 Bean。
     *
     * @param name Bean 的名称
     * @return 返回 Bean 实例，如果找不到，则返回 null
     */
    public static <T> T getBean(String name){
        try {
            return (T) springContext.getBean(name);
        } catch (NoSuchBeanDefinitionException e) {
            return null;
        }
    }

    /**
     * 根据别名获取 Bean。
     * 如果找到别名，则使用对应的原始 Bean 名称来获取 Bean。
     *
     * @param aliasName Bean 的别名
     * @return 返回 Bean 实例
     * @throws RuntimeException 如果别名在注册表中不存在，则抛出异常
     */
    public static <T> T getBeanByAliasName(String aliasName){
        String originBeanName = BEAN_SPI_NAME.get(aliasName);
        if (StringUtils.isEmpty(originBeanName)){
            throw new RuntimeException("can not find bean for alias name ["+aliasName+"] , because it not exist");
        }

        return getBean(originBeanName);
    }


    /**
     * 根据名称或别名从应用程序上下文中获取 Bean。
     * 首先尝试根据名称获取 Bean，如果找不到，则根据别名查找原始 Bean 名称。
     *
     * @param name Bean 的名称或别名
     * @return 返回 Bean 实例
     * @throws RuntimeException 如果该名称或别名的 Bean 不存在，则抛出异常
     */
    public static <T> T getBeanByAllName(String name){
        try {
            return (T) springContext.getBean(name);
        } catch (NoSuchBeanDefinitionException e) {
            String originBeanName = BEAN_SPI_NAME.get(name);
            if (StringUtils.isEmpty(originBeanName)){
                throw new RuntimeException("can not find bean for alias name ["+name+"] , because it not exist");
            }

            return getBean(originBeanName);
        }
    }

    /**
     * 根据类类型从应用程序上下文中获取 Bean。
     *
     * @param clz Bean 的类类型
     * @return 返回 Bean 实例，如果找不到，则返回 null
     */
    public static <T> T getBean(Class<T> clz) {
        try {
            return springContext.getBean(clz);
        } catch (NoSuchBeanDefinitionException e) {
          return null;
        }
    }

    /**
     * 设置由 Spring 提供的 ApplicationContext 实例。
     * 该方法在 Spring 初始化过程中被调用。
     *
     * @param applicationContext 要设置的应用程序上下文
     * @throws BeansException 如果在设置应用程序上下文时发生错误，抛出异常
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        if (springContext == null){
            springContext = applicationContext;
        }
    }
}
